package com.jingxuan.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.jingxuan.common.constant.RedisKeyConstants;
import com.jingxuan.dto.response.BrowseHistoryItemResponse;
import com.jingxuan.dto.response.BrowseHistoryResponse;
import com.jingxuan.entity.Product;
import com.jingxuan.mapper.ProductsMapper;
import com.jingxuan.mapper.RecommendMapper;
import com.jingxuan.mapper.RecommendMapper.ProductFeatureDto;
import com.jingxuan.service.UserBehaviorService;
import com.jingxuan.util.UserBehaviorAlgorithm;
import com.jingxuan.util.UserBehaviorAlgorithm.BrowseRecord;
import com.jingxuan.util.UserBehaviorAlgorithm.GlobalBehaviorStats;
import com.jingxuan.util.UserBehaviorAlgorithm.SearchRecord;
import com.jingxuan.util.UserBehaviorAlgorithm.UserBehaviorSummary;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 用户行为服务实现，使用 Redis 缓存搜索与浏览记录，并构建全局统计。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserBehaviorServiceImpl implements UserBehaviorService {

    private static final String GLOBAL_USERS_KEY = "behavior:users";
    private static final String GLOBAL_KEYWORD_HASH = "behavior:stats:keyword";
    private static final String GLOBAL_PRODUCT_HASH = "behavior:stats:product";
    private static final String GLOBAL_CATEGORY_HASH = "behavior:stats:category";
    private static final String CATEGORY_USERS_PREFIX = "behavior:category:users:";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ProductsMapper productsMapper;
    @Autowired
    private final RecommendMapper recommendMapper;

    /**
     * 记录用户搜索行为。
     */
    @Override
    public void recordSearchBehavior(SearchRecord record) {
        if (!UserBehaviorAlgorithm.isValidSearch(record)) {
            return;
        }
        Long userId = record.getUserId();
        if (userId == null) {
            return;
        }
        record.setTimestamp(Optional.ofNullable(record.getTimestamp()).orElse(LocalDateTime.now()));

        UserBehaviorSummary summary = loadSummary(userId);
        UserBehaviorAlgorithm.appendSearchRecord(summary, record);
        summary.setUserId(userId);
        saveSummary(userId, summary);

        updateGlobalUserSet(userId);
        updateKeywordStats(userId, record.getKeyword());
    }

    /**
     * 记录用户浏览行为。
     */
    @Override
    public void recordBrowseBehavior(BrowseRecord record) {
        if (record == null || record.getUserId() == null || record.getProductId() == null) {
            return;
        }
        BrowseRecord enriched = enrichBrowseRecord(record);
        if (!UserBehaviorAlgorithm.isValidBrowse(enriched)) {
            return;
        }

        Long userId = enriched.getUserId();
        UserBehaviorSummary summary = loadSummary(userId);
        removeExistingBrowseRecord(summary, enriched.getProductId());
        UserBehaviorAlgorithm.appendBrowseRecord(summary, enriched);
        summary.setUserId(userId);
        saveSummary(userId, summary);

        updateGlobalUserSet(userId);
        updateProductStats(userId, enriched);
        updateCategoryStats(userId, enriched.getCategory());
    }

    /**
     * 获取用户的行为摘要画像。
     */
    @Override
    public UserBehaviorSummary getUserBehaviorSummary(Long userId) {
        if (userId == null) {
            return UserBehaviorSummary.builder().build();
        }
        return loadSummary(userId);
    }

    @Override
    public BrowseHistoryResponse getBrowseHistory(Long userId, int limit) {
        BrowseHistoryResponse response = new BrowseHistoryResponse();
        if (userId == null) {
            return response;
        }
        int resolvedLimit = limit <= 0 ? 30 : Math.min(limit, 100);

        UserBehaviorSummary summary = loadSummary(userId);
        List<BrowseRecord> browseRecords = summary.getBrowseRecords();
        if (browseRecords == null || browseRecords.isEmpty()) {
            return response;
        }

        List<BrowseRecord> sorted = new ArrayList<>(browseRecords);
        sorted.sort(Comparator.comparing(this::resolveRecordTime));

        int total = sorted.size();
        int fromIndex = Math.max(total - resolvedLimit, 0);
        List<BrowseRecord> latest = new ArrayList<>(sorted.subList(fromIndex, total));
        Collections.reverse(latest);

        Set<String> productIds = latest.stream()
            .map(BrowseRecord::getProductId)
            .filter(Objects::nonNull)
            .collect(Collectors.toCollection(LinkedHashSet::new));

        Map<String, Product> productMap = productIds.isEmpty()
            ? Collections.emptyMap()
            : productsMapper.selectByIds(new ArrayList<>(productIds)).stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(Product::getId, product -> product));

        List<BrowseHistoryItemResponse> items = latest.stream()
            .map(record -> toHistoryItem(record, productMap.get(record.getProductId())))
            .collect(Collectors.toList());

        response.setRecords(items);
        response.setTotal(total);
        response.setHasMore(total > resolvedLimit);
        return response;
    }

    private void removeExistingBrowseRecord(UserBehaviorSummary summary, String productId) {
        if (summary == null || productId == null || productId.isBlank()) {
            return;
        }
        List<BrowseRecord> records = summary.getBrowseRecords();
        if (records == null || records.isEmpty()) {
            return;
        }
        records.removeIf(record -> productId.equals(record.getProductId()));
    }

    /**
     * 获取全局行为统计数据。
     */
    @Override
    public GlobalBehaviorStats getGlobalBehaviorStats() {
        long totalUsers = Optional.ofNullable(redisTemplate.opsForSet().size(GLOBAL_USERS_KEY)).orElse(0L);

        Map<String, Long> keywordCounts = readHashLongMap(GLOBAL_KEYWORD_HASH);
        Map<String, Long> productCounts = readHashLongMap(GLOBAL_PRODUCT_HASH);
        Map<String, Long> categoryCounts = readHashLongMap(GLOBAL_CATEGORY_HASH);

        return GlobalBehaviorStats.builder()
            .totalUsers(totalUsers)
            .keywordUserCount(new ConcurrentHashMap<String, Long>(keywordCounts))
            .productUserCount(new ConcurrentHashMap<String, Long>(productCounts))
            .categoryUserCount(new ConcurrentHashMap<String, Long>(categoryCounts))
            .build();
    }

    private LocalDateTime resolveRecordTime(BrowseRecord record) {
        if (record == null) {
            return LocalDateTime.MIN;
        }
        if (record.getEndTime() != null) {
            return record.getEndTime();
        }
        if (record.getStartTime() != null) {
            return record.getStartTime();
        }
        return LocalDateTime.MIN;
    }

    private BrowseHistoryItemResponse toHistoryItem(BrowseRecord record, Product product) {
        BrowseHistoryItemResponse item = new BrowseHistoryItemResponse();
        item.setProductId(record.getProductId());
        item.setProductName(Optional.ofNullable(record.getProductName())
            .orElseGet(() -> product != null ? product.getProductName() : null));
        item.setCategory(Optional.ofNullable(record.getCategory())
            .orElseGet(() -> product != null ? product.getType() : null));
        item.setBrand(Optional.ofNullable(record.getBrand())
            .orElseGet(() -> product != null ? extractBrand(product.getProductName()) : null));
        item.setPrice(Optional.ofNullable(record.getPrice())
            .orElseGet(() -> product != null ? product.getPrice() : null));
        item.setPositiveRate(record.getPositiveRate());
        item.setHeat(record.getHeat());
        item.setReleaseTime(Optional.ofNullable(record.getReleaseTime())
            .orElseGet(() -> product != null ? product.getCreatedAt() : null));
        item.setStartTime(record.getStartTime());
        item.setEndTime(record.getEndTime());
        item.setDurationSeconds(record.getDurationSeconds());
        item.setPageDepth(record.getPageDepth());
        item.setPageCount(record.getPageCount());
        if (product != null) {
            item.setImageUrl(product.getPictureUrl());
            item.setPlatformName(resolvePlatformName(product.getPlatformId()));
            item.setLink(product.getLink());
        }
        item.setViewedAt(resolveRecordTime(record));
        return item;
    }

    private String resolvePlatformName(Integer platformId) {
        if (platformId == null) {
            return "未知平台";
        }
        return switch (platformId) {
            case 1 -> "淘宝";
            case 2 -> "京东";
            default -> "未知平台";
        };
    }

    /**
     * 从redis中读取hashMap。
     */
    private Map<String, Long> readHashLongMap(String key) {
        Map<Object, Object> raw = redisTemplate.opsForHash().entries(key);
        if (raw == null || raw.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<String, Long> result = new HashMap<>(raw.size());
        raw.forEach((hashKey, value) -> {
            if (hashKey != null && value != null) {
                long count = value instanceof Number ? ((Number) value).longValue() : Long.parseLong(value.toString());
                result.put(hashKey.toString(), count);
            }
        });
        return result;
    }

    /**
     * 从redis中加载用户行为摘要。
     */
    private UserBehaviorSummary loadSummary(Long userId) {
        String key = RedisKeyConstants.USER_BEHAVIOR_PREFIX + userId;
        Object cached = redisTemplate.opsForValue().get(key);
        if (cached instanceof UserBehaviorSummary) {
            return (UserBehaviorSummary) cached;
        }
        UserBehaviorSummary summary = new UserBehaviorSummary();
        summary.setUserId(userId);
        return summary;
    }

    /**
     * 将用户行为摘要保存到redis中。
     */
    private void saveSummary(Long userId, UserBehaviorSummary summary) {
        String key = RedisKeyConstants.USER_BEHAVIOR_PREFIX + userId;
        try {
            redisTemplate.opsForValue().set(key, summary);
        } catch (DataAccessException ex) {
            log.warn("保存用户行为摘要失败, userId={}", userId, ex);
        }
    }

    /**
     * 更新全局用户集合。
     */
    private void updateGlobalUserSet(Long userId) {
        redisTemplate.opsForSet().add(GLOBAL_USERS_KEY, userId);
    }

    /**
     * 更新关键词统计。
     */
    private void updateKeywordStats(Long userId, String keyword) {
        if (keyword == null) {
            return;
        }
        String normalized = keyword.trim().toLowerCase();
        String key = RedisKeyConstants.KEYWORD_USERS_PREFIX + normalized;
        Long added = redisTemplate.opsForSet().add(key, userId);
        if (added != null && added > 0) {
            redisTemplate.opsForHash().increment(GLOBAL_KEYWORD_HASH, normalized, 1L);
        }
    }

    /**
     * 更新产品统计。
     */
    private void updateProductStats(Long userId, BrowseRecord record) {
        String productId = record.getProductId();
        if (productId == null) {
            return;
        }
        String key = RedisKeyConstants.PRODUCT_USERS_PREFIX + productId;
        Long added = redisTemplate.opsForSet().add(key, userId);
        if (added != null && added > 0) {
            redisTemplate.opsForHash().increment(GLOBAL_PRODUCT_HASH, productId, 1L);
        }
    }

    /**
     * 更新品类统计。
     */
    private void updateCategoryStats(Long userId, String category) {
        if (category == null || category.isBlank()) {
            return;
        }
        String normalized = category.trim().toLowerCase();
        String key = CATEGORY_USERS_PREFIX + normalized;
        Long added = redisTemplate.opsForSet().add(key, userId);
        if (added != null && added > 0) {
            redisTemplate.opsForHash().increment(GLOBAL_CATEGORY_HASH, normalized, 1L);
        }
    }

    /**
     * 丰富浏览记录。
     */
    private BrowseRecord enrichBrowseRecord(BrowseRecord record) {
        LocalDateTime start = record.getStartTime() != null ? record.getStartTime() : LocalDateTime.now();
        LocalDateTime end = record.getEndTime() != null ? record.getEndTime() : start.plusSeconds(3);
        if (end.isBefore(start)) {
            end = start.plusSeconds(3);
        } else if (Duration.between(start, end).toMillis() < 3000) {
            end = start.plusSeconds(3);
        }
        record.setStartTime(start);
        record.setEndTime(end);

        int depth = Optional.ofNullable(record.getPageDepth()).orElse(2);
        if (depth < 2) {
            depth = 2;
        }
        record.setPageDepth(depth);

        int pageCount = Optional.ofNullable(record.getPageCount()).orElse(depth);
        if (pageCount < depth) {
            pageCount = depth;
        } else if (pageCount < 1) {
            pageCount = 1;
        }
        record.setPageCount(pageCount);

        Product product = productsMapper.selectById(record.getProductId());
        if (product != null) {
            if (record.getProductName() == null) {
                record.setProductName(product.getProductName());
            }
            if (record.getCategory() == null) {
                record.setCategory(product.getType());
            }
            if (record.getPrice() == null) {
                record.setPrice(product.getPrice());
            }
            if (record.getReleaseTime() == null) {
                record.setReleaseTime(product.getCreatedAt());
            }
            if (record.getBrand() == null && product.getProductName() != null) {
                record.setBrand(extractBrand(product.getProductName()));
            }
        }

        List<ProductFeatureDto> featureDtos = recommendMapper.getProductFeaturesByIds(Collections.singletonList(record.getProductId()));
        if (!featureDtos.isEmpty()) {
            ProductFeatureDto feature = featureDtos.get(0);
            if (record.getPositiveRate() == null && feature.getPositiveRate() != null) {
                record.setPositiveRate(feature.getPositiveRate());
            }
            double heat = computeHeatScore(feature.getCommentCount(), feature.getAverageLikes());
            if (record.getHeat() == null) {
                record.setHeat(heat);
            }
            if (record.getReleaseTime() == null) {
                record.setReleaseTime(feature.getLastCommentTime());
            }
            if (record.getCategory() == null) {
                record.setCategory(feature.getProductType());
            }
        }

        if (record.getHeat() == null) {
            record.setHeat(0.5D);
        }
        if (record.getPositiveRate() == null) {
            record.setPositiveRate(0.6D);
        }
        if (record.getBrand() == null) {
            record.setBrand("unknown");
        }
        return record;
    }

    /**
     * 计算热度得分。
     */
    private double computeHeatScore(Integer commentCount, Double averageLikes) {
        double comments = commentCount == null ? 0D : commentCount.doubleValue();
        double likes = averageLikes == null ? 0D : averageLikes;
        return Math.log1p(comments) * 0.5D + Math.log1p(likes) * 0.5D;
    }

    /**
     * 提取品牌。
     */
    private String extractBrand(String productName) {
        if (productName == null || productName.isBlank()) {
            return "unknown";
        }
        String normalized = productName.trim();
        int delimiter = findBrandDelimiter(normalized);
        String candidate = delimiter > 0 ? normalized.substring(0, delimiter) : normalized;
        candidate = candidate.replaceAll("[\\[\\]（）()·]", "").trim();
        return candidate.isEmpty() ? "unknown" : candidate.toLowerCase();
    }

    /**
     * 查找品牌分隔符。
     */
    private int findBrandDelimiter(String name) {
        int space = name.indexOf(' ');
        int dash = name.indexOf('-');
        int middleDot = name.indexOf('·');
        int bracket = name.indexOf('（');
        int bracket2 = name.indexOf('(');
        int delimiter = Integer.MAX_VALUE;
        delimiter = space > 0 ? Math.min(delimiter, space) : delimiter;
        delimiter = dash > 0 ? Math.min(delimiter, dash) : delimiter;
        delimiter = middleDot > 0 ? Math.min(delimiter, middleDot) : delimiter;
        delimiter = bracket > 0 ? Math.min(delimiter, bracket) : delimiter;
        delimiter = bracket2 > 0 ? Math.min(delimiter, bracket2) : delimiter;
        return delimiter == Integer.MAX_VALUE ? -1 : delimiter;
    }
}
